#include "GameApp.h"
#include <iostream>

using namespace std;

GameApp::GameApp(void)
{
	m_hWindow			= NULL;
	m_usLastFrameRate	= 0;
	m_usFrameRate		= 0;
	m_bLostDevice		= FALSE;
	m_bActiveRender		= TRUE;

	m_pD3D				= NULL;
	m_pD3DDevice		= NULL;
}

GameApp::~GameApp()
{
	
}

BOOL GameApp::GameInit(HINSTANCE hInstance)
{
	if (!InitWindow(hInstance)) 
		return FALSE;

	if (!InitDirect3D()) 
		return FALSE;

	if (!InitResource())
		return FALSE;
	return TRUE;
}

BOOL GameApp::InitWindow(HINSTANCE hInstance)
{
	this->m_hInstance = hInstance;
	// Register to windows
	WNDCLASS wndclass;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.hCursor = LoadCursor(NULL, IDI_APPLICATION);
	wndclass.hIcon = LoadIcon(NULL, IDC_ARROW);
	wndclass.hInstance = hInstance;
	wndclass.lpfnWndProc = StaticWinProc;
	wndclass.lpszClassName = "Init Device";
	wndclass.lpszMenuName = NULL;
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	if (FAILED(RegisterClass(&wndclass)))
		return FALSE;

	// Create window WS_EX_TOPMOST | WS_POPUP
	m_hWindow = CreateWindow("Init Device", 
						   "Init Device",
						   WS_OVERLAPPEDWINDOW
						   ,
						   0,
						   0,
						   800,
						   600,
						   NULL,
						   NULL,
						   hInstance,
						   this
						   );
	if (!m_hWindow)
		return FALSE;

	ShowWindow(m_hWindow, SW_SHOW);

	return TRUE;	
}

BOOL GameApp::InitResource()
{
	// Init Resource Manager
	CResourceManager* resource = CResourceManager::GetInstance();
	resource->Init(this->m_pD3DDevice);
	
	// Init game engine
	this->m_gameEngine = new CGameEngine();
	this->m_gameEngine->Init(this->m_pD3DDevice, this->m_hInstance, this->m_hWindow);
	return TRUE;
}


void GameApp::GameRun()
{
	MSG msg;

	while (1)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT)
				break;
			TranslateMessage(&msg);
			DispatchMessage(&msg);

		}
		else
		{
			FrameAdvance();
		}
	}
}

BOOL GameApp::InitDirect3D()
{
	D3DPRESENT_PARAMETERS D3DPresentParams;
	D3DCAPS9 Caps;

	m_pD3D = Direct3DCreate9(D3D_SDK_VERSION);

	// Fulfill the PrensentParameters
	ZeroMemory(&D3DPresentParams, sizeof(D3DPRESENT_PARAMETERS));
	D3DPresentParams.BackBufferCount = 1;
	D3DPresentParams.BackBufferFormat = D3DFMT_UNKNOWN;//D3DFMT_UNKNOWND3DFMT_X8R8G8B8
	//D3DPresentParams.BackBufferWidth = 800;
	//D3DPresentParams.BackBufferHeight = 600;
	D3DPresentParams.hDeviceWindow = m_hWindow;
	D3DPresentParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
	D3DPresentParams.Windowed = TRUE;
	D3DPresentParams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

	unsigned long ulFlag = D3DCREATE_SOFTWARE_VERTEXPROCESSING;

	// Check if Hardware T&L is available
	ZeroMemory(&Caps, sizeof(D3DCAPS9));
	m_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &Caps);
	if (Caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
		ulFlag = D3DCREATE_HARDWARE_VERTEXPROCESSING;

	// Attempt to create a HAL Device
	if (FAILED(m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWindow, ulFlag, &D3DPresentParams, &m_pD3DDevice)))
	{

		// Check if Hardware T&L is available
		m_pD3D->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, &Caps);
		if (Caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
			ulFlag = D3DCREATE_HARDWARE_VERTEXPROCESSING;

		// Attempt to Create a REF Device
		if (FAILED (m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, m_hWindow, ulFlag, &D3DPresentParams, &m_pD3DDevice)))
		{
			return FALSE;
		}
	}

	if (FAILED(D3DXCreateSprite(this->m_pD3DDevice, &this->m_pSpriterHandler)))
	{
		return FALSE;
	}

	m_D3DPresentParams = D3DPresentParams;

	return TRUE;
}

void GameApp::FrameAdvance()
{
	// Advance the timer
	m_Timer.Tick(0);
	
	if (!m_bActiveRender) 
		return;

	// Get and present FPS
	m_usFrameRate = m_Timer.GetFrameRate();
	if (m_usFrameRate != m_usLastFrameRate)
	{
		static TCHAR FPS[20];
		m_Timer.GetFrameRate(FPS);
		m_usLastFrameRate = m_usFrameRate;
		SetWindowText(m_hWindow, FPS);
	}	

	// Recover lost device
	if (m_bLostDevice == TRUE)
	{
		HRESULT hr = m_pD3DDevice->TestCooperativeLevel();
		if (hr == D3DERR_DEVICENOTRESET)
		{
			m_pD3DDevice->Reset(&m_D3DPresentParams);
			m_bLostDevice = false;
		}
		else
		{
			return;
		}
	}

	// Update sprte
	//this->m_sprite->Update(this->m_Timer.GetGameTime());
	this->m_gameEngine->Update(this->m_Timer.GetTimeElased());

	// Clear and Render
	m_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0);
	m_pD3DDevice->BeginScene();
	this->m_pSpriterHandler->Begin(D3DXSPRITE_ALPHABLEND);

	//this->m_pSpriterHandler->Draw(this->m_texture, NULL, NULL, &D3DXVECTOR3(10, 10, 0), D3DCOLOR_ARGB(255, 255, 255, 255));
	//this->m_sprite->Draw(this->m_pSpriterHandler);
	this->m_gameEngine->Draw(this->m_pSpriterHandler);
	this->m_pSpriterHandler->End();
	m_pD3DDevice->EndScene();
	if (FAILED(m_pD3DDevice->Present(NULL, NULL, NULL, NULL)))
		m_bLostDevice = TRUE;
}

void GameApp::GameEnd()
{
	if (m_pD3D)
	{
		m_pD3D->Release();
		m_pD3D = NULL;
	}
	if (m_pD3DDevice)
	{
		m_pD3DDevice->Release();
		m_pD3DDevice = NULL;
	}
	if (m_hWindow)
	{
		DestroyWindow(m_hWindow);	
		m_hWindow = NULL;
	}
}

LRESULT CALLBACK GameApp::StaticWinProc(HWND hWindow, UINT iMessage, WPARAM wParam, LPARAM lParam)
{	
	if ( iMessage == WM_CREATE ) 
		SetWindowLong( hWindow, GWL_USERDATA, (LONG)((CREATESTRUCT FAR *)lParam)->lpCreateParams);

	GameApp *MyGame = (GameApp*)GetWindowLong( hWindow, GWL_USERDATA );

	if (MyGame) 
		return MyGame->WinProc( hWindow, iMessage, wParam, lParam );

	return DefWindowProc(hWindow, iMessage, wParam, lParam);
}

LRESULT GameApp::WinProc(HWND hWindow, UINT iMessage, WPARAM wParam, LPARAM lParam)
{
	switch (iMessage)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	case WM_SIZE:
		if (wParam == SIZE_MINIMIZED)
		{
			// Stop render when minimize
			m_bActiveRender = FALSE;
		}
		else
		{
			m_bActiveRender = TRUE;
		}
		break;
	}
	return DefWindowProc(hWindow, iMessage, wParam, lParam);
}